/**
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 *         
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. Unless required by applicable law or
 * agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 */
package org.rhok.pdx.ncn.background;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.rhok.pdx.ncn.application.Common;
import org.rhok.pdx.ncn.common.Utils;
import org.rhok.pdx.ncn.communicator.WebClient;
import org.rhok.pdx.ncn.datatypes.LocationData;

/**
 * Background process to communicate with the server at regular intervals to both submit data as
 * well as receive new updates from the server.
 * @author pfritzsche
 *
 */
public class ServerSyncerThread extends Thread {

	private static final long
		MIN_DELAY = TimeUnit.MINUTES.toMillis(1),
		MAX_DELAY = TimeUnit.HOURS.toMillis(1),
		TEN_SECONDS = TimeUnit.SECONDS.toMillis(30);
	private static final WebClient client = new WebClient();

	private boolean running = true;
	private long delay = MIN_DELAY;
	
	/**
	 * Thread runner. Periodically syncs with the server, submitting new data points and downloading
	 * updates from the server. Uses additive increase, multiplicative decrease for the time delay
	 * between attempted syncs to try and save battery.
	 */
	public void run() {
		while (running) {
		    Common.db().open();
		    LocationData mostRecentData = Common.db().getMostRecentLocationData();
		    try {
		        List<LocationData> locs = client.getServerData(mostRecentData);
		        boolean wasSuccessful = locs != null;
		        updateDelay(wasSuccessful);
		        if (wasSuccessful) {
		            Common.db().addRows(locs);
		            Utils.updateLocation(locs);
		        }
		        Thread.sleep(delay);
		    } catch (Exception e) {
		        e.printStackTrace();
		    }
		}
		Common.db().close();
	}
	
	/**
	 * Updates the current amount of time being used for the delay
	 * @param result true if the previous sync attempt succeeded
	 */
	private void updateDelay(boolean result) {
		if (!result) {
			if (delay <= MAX_DELAY) {
				delay = (delay > MAX_DELAY / 2) ? MAX_DELAY : delay * 2;
			}
		} else {
			if (delay >= MIN_DELAY) {
				delay = (delay < MIN_DELAY + TEN_SECONDS) ? MIN_DELAY : delay - TEN_SECONDS;
			}
		}
	}
	
	public void cancel() {
	    running = false;
	}
}
